#!/usr/bin/env python
# -*-coding:utf-8 -*-

__all__ = ['TimeWindow', 'backupForEdgesOnPath', 'restoreForEdgesOnPath']

import logging
import numpy as np


class TimeWindow(object):
    """
    时间窗。
    参考文献：
    Smolic-Rocak N, Bogdan S, Kovacic Z, et al. Time Windows Based Dynamic Routing in Multi-AGV Systems[J]. 
    IEEE Transactions on Automation Science & Engineering, 2009, 7(1):151-155.
    """
    
    def __init__(self, map, robots_num):
        """
        创建一个时间窗。
        :param edges: 所有边。
        :param robots_num: 机器人的数量。
        """
        self.initLogger()
        self.w = {}
        self.t_i = {}
        self.t_o = {}
        # backups of the above quantities
        self._w = {}
        self._t_i = {}
        self._t_o = {}
        
        edges = map.getAllEdges()
        self._map = map
        self._edges = []
        self._robots_num = robots_num
        for e in edges:
            # Since the edges (a, b) and (b, a) are the same resource,
            # we need to remove ambiguities.
            e = self.disambiguate(e)
            self._edges.append(e)
            if e not in self.w:
                self.w[e]   = np.zeros((robots_num,))
                self.t_i[e] = np.ones((robots_num,))*float('inf')
                self.t_o[e] = np.ones((robots_num,))*float('inf')
                self._w[e]   = np.zeros((robots_num,))
                self._t_i[e] = np.ones((robots_num,))*float('inf')
                self._t_o[e] = np.ones((robots_num,))*float('inf')
                
    def initLogger(self):
        self._logger = logging.getLogger('MyTCS.TimeWindow')
        
    def disambiguate(self, e):
        if self._map.isEdgeBidirectional(e):
            return (min(e[0], e[1]), max(e[0], e[1]))
        return e

    def backupForEdgesOnPath(self, p):
        """
        为路径p上所有的边，进行时间窗的备份。
        :param p: 待备份的路径p。
        """
        for i in range(len(p)-1):
            e = self.disambiguate((p[i], p[i+1]))
            np.copyto(self._w[e], self.w[e])
            np.copyto(self._t_i[e], self.t_i[e])
            np.copyto(self._t_o[e], self.t_o[e])

    def restoreForEdgesOnPath(self, p):
        """
        恢复备份的时间窗。
        :param p: 待恢复的路径p。
        """
        for i in range(len(p)-1):
            e = self.disambiguate((p[i], p[i+1]))
            np.copyto(self.w[e], self._w[e])
            np.copyto(self.t_i[e], self._t_i[e])
            np.copyto(self.t_o[e], self._t_o[e])
    
    def copy(self):
        to = TimeWindow(self._map, self._robots_num)
        self.copyto(to)
        return to

    def copyto(self, to):
        for e in self._edges:
            np.copyto(to.w[e], self.w[e])
            np.copyto(to.t_i[e], self.t_i[e])
            np.copyto(to.t_o[e], self.t_o[e])
            
    def clear(self):
        for e in self._edges:
            self.w[e].fill(0)
            self.t_i[e].fill('inf')
            self.t_o[e].fill('inf')
            self._w[e].fill(0)
            self._t_i[e].fill('inf')
            self._t_o[e].fill('inf')
    
    def printForEdge(self, e):
        self._logger.debug('edge {0}'.format(e))
        e = self.disambiguate(e)
        t_i_sorted_idx = np.argsort(self.t_i[e])
        t_o_sorted_idx = np.argsort(self.t_o[e])
        #t_i_sorted = sorted(self.t_i[e])
        #t_o_sorted = sorted(self.t_o[e])
        for i in range(len(t_i_sorted_idx)):
            i_idx = t_i_sorted_idx[i]
            o_idx = t_o_sorted_idx[i]
            if self.t_i[e][i_idx] == float('inf'):
                break
            self._logger.debug('in: {0:.2f} out: {1:.2f}, robot: {2}'.format(self.t_i[e][i_idx], self.t_o[e][o_idx], i_idx))
    
    def printForAll(self):
        for e in self.t_i.keys():
            self.printForEdge(e)
            
    def isEdgeOccupied(self, e):
        e = self.disambiguate(e)
        return np.max(self.w[e]) > 1e-6 or np.min(self.t_i[e]) < float('inf')
        
    def printForOccupiedEdges(self):
        for e in self.t_i.keys():
            if self.isEdgeOccupied(e):
                self.printForEdge(e)
            
            